﻿using FastEndpoints;
using LogCenterService.Models;
using Microsoft.AspNetCore.Diagnostics;
using System.Net;

namespace LogCenterService.Common.Extension;

internal class ExceptionHandler;

/// <summary>
/// 【端点内】全局异常处理
/// </summary>
public static class ExceptionHandlerExtensions
{
    /// <summary>
    /// 注册一个全局异常处理器，该处理器将在服务器上记录异常，并在发生未处理的异常时向客户端返回用户友好的json响应。
    /// 提示：当使用此异常处理器时，你可能希望关闭asp.net core异常中间件的日志记录以避免重复，如下所示：
    /// <code>
    /// "Logging": { "LogLevel": { "Default": "Warning", "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None" } }
    /// </code>
    /// </summary>
    /// <param name="app"></param>
    /// <param name="logger">一个可选的日志记录器实例</param>
    /// <param name="logStructuredException">如果你希望以结构化的方式记录错误，则设置为true</param>
    /// <param name="useGenericReason">如果你不希望在发送给客户端的json响应中暴露实际的异常原因，则设置为true</param>
    public static IApplicationBuilder UseAPIExceptionHandler(this IApplicationBuilder app,
                                                                 ILogger? logger = null,
                                                                 bool logStructuredException = false,
                                                                 bool useGenericReason = false)
    {
        app.UseExceptionHandler(
            errApp =>
            {
                errApp.Run(
                    async ctx =>
                    {
                        var exHandlerFeature = ctx.Features.Get<IExceptionHandlerFeature>();

                        if (exHandlerFeature is not null)
                        {
                            logger ??= ctx.Resolve<ILogger<ExceptionHandler>>();
                            var route = exHandlerFeature.Endpoint?.DisplayName?.Split(" => ")[0];
                            var exceptionType = exHandlerFeature.Error.GetType().Name;
                            var reason = exHandlerFeature.Error.Message;

                            if (logStructuredException)
                                logger.LogError(exHandlerFeature.Error, "[{@exceptionType}] at [{@route}] due to [{@reason}]", exceptionType, route, reason);
                            else
                            {
                                //该分支仅用于非结构化文本日志记录
                                logger.LogError(
                                    $"""
                                     =================================
                                     {route}
                                     TYPE: {exceptionType}
                                     REASON: {reason}
                                     ---------------------------------
                                     {exHandlerFeature.Error.StackTrace}
                                     """);
                            }

                            ctx.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                            ctx.Response.ContentType = "application/problem+json";
                            //await ctx.Response.WriteAsJsonAsync(
                            //    new InternalErrorResponse
                            //    {
                            //        Status = "内部服务异常！",
                            //        Code = ctx.Response.StatusCode,
                            //        Reason = useGenericReason ? "发生了意外错误" : reason,
                            //        Note = "有关堆栈跟踪，请参阅应用程序日志"
                            //    });
                            await ctx.Response.WriteAsJsonAsync(Response.Error(
                                message: "内部服务异常！",
                                data: useGenericReason ? "发生了意外错误" : reason
                                ));
                        }
                    });
            });

        return app;
    }
}